/**Imgage View 3.6b- image browser applet
Copyright (C) 1998, 1999  Mihai Munteanu

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

You can contact me at:

* http://www.obs-us.com/people/mihai
* e-mail:  m.munteanu@shef.ac.uk   or   mmm@deltanet.roknet.ro
* Mihai Munteanu
  str. M.Eminescu 44
  2200 Brasov, ROMANIA
* fax: +40 (0)68 410330

Image View was created and compiled using only the Sun JDK 1.0.2

*/


import java.awt.*;
import java.awt.image.*;
import java.awt.image.ImageObserver;
import java.net.*;
import java.applet.*;
import java.lang.Math;
import java.io.*;
import java.util.*;
import mmmUtil.ReadParam;
import mmmUtil.HelpWindow;

public  class ImgView extends Applet {

	static String HOMEPAGE="http://www.obs-us.com/people/mihai/";
	static String VERSION="Image View 3.6b";
	static String AUTHOR="Mihai Munteanu";
	static String COPYRIGHT="(c) 1998, 1999";
	Image photo; //The curent image
	int width, heigth; //the width and th heigth of the curent image
	Applet applet; //This applet
	boolean sizeknown; //True when the size of the image is known
	boolean errored, err;
	boolean tooBig, tooSmall, fitOn, oneto1On, appletToSmall; //some flags
	char layoutStyle; //the layout type
	
	//Graphical interface
	Button btnNext, btnPrev, btnFit, btn1to1, btnZoomIn, btnZoomOut, btnInfo;
	Panel pBtn, pSouth, pZoom, pControl, pTitle, p1Title, pEast;
	List photoList;
	Scrollbar vertScroll =new Scrollbar(Scrollbar.VERTICAL);
	Scrollbar horizScroll=new Scrollbar(Scrollbar.HORIZONTAL);
	Label labTitle;
	Color backCol, prevNextBtnCol, btnCol, labCol, listCol;
	Font fBig;
	Font fSmall;
	Font fSmallB;
	HelpWindow helpWindow = new HelpWindow();
	
	String[] fileName; //array that contains the filenames of the images
	String[] comments; //array that oontains the comment (caption) for each image
	boolean[] notFound; //true if an image was not found

	float pwFit, phFit, pw1to1, ph1to1, phZoom, pwZoom;
	int   px, py, ph, pw, pxFit, pyFit, px1to1, py1to1, pxZoom, pyZoom, maxZoom;

	int noOfPhotos = 0; // is incremented for every file name found in ImgView.toc
	int pointPhoto = 0; //curent image index
	int numberOfPhotos = 1024; // this is used to allocate memory for the fileName and comments arrays.
				   // default for 1024 photos

	int picW, picH; //curent image dysplay size
	String about;//the about text

public void init() {

	//set the flags
	sizeknown=false;
	tooBig=false;
	tooSmall=false;
	oneto1On=false;
	fitOn=true;
	appletToSmall=false;
	layoutStyle='l';
	
	//set the about text
	about=setAboutText();
	
	//and the about window
	helpWindow.setTitle(VERSION+" - Help");
	helpWindow.setText(about);
	helpWindow.pack();
	helpWindow.resize(500,380);
	helpWindow.setResizable(false);
	
	//set the fonts
	fBig = new Font("Helvetica", Font.BOLD, 14);
	fSmall=new Font("Helvetica", Font.PLAIN, 12);
	fSmallB=new Font("Helvetica", Font.BOLD, 12);
	
	//set the colors
	backCol=ReadParam.readColor(getParameter("backColor") ,Color.black);
	btnCol=ReadParam.readColor(getParameter("buttonsColor") ,Color.lightGray);
	labCol=ReadParam.readColor(getParameter("titlesColor") ,Color.yellow);
	listCol=ReadParam.readColor(getParameter("listBackColor") ,Color.white);
	prevNextBtnCol=ReadParam.readColor(getParameter("buttonsPrevNextColor") ,Color.white);

	if(size().width<240 || size().height<180){//checks the size of th eapplet
		appletToSmall=true;
	}

	//check the layout type
	String layoutType = "";	
	layoutType = layoutType + getParameter("layouttype");
	if(layoutType.length()>0){
		if(layoutType.charAt(0)=='c' || layoutType.charAt(0)=='C'){
			layoutStyle='c';
		}
		if(layoutType.charAt(0)=='r' || layoutType.charAt(0)=='R'){
			layoutStyle='r';
		}
	}

	//set the images arrays
	numberOfPhotos=ReadParam.readPositiveInt(getParameter("numberOfPhotos"), 1024); //default number of photos is 1024
	maxZoom=ReadParam.readPositiveInt(getParameter("maxZoomIn"), 3); //default maximum zoom is 3
	photoList =new List(4, false); 
	photoList.setBackground(listCol);

	fileName = new String[numberOfPhotos];
	comments = new String[numberOfPhotos];
	notFound = new boolean[numberOfPhotos];	
		
	try{//this block will read and scan the ImgView.toc file
		URL tocfile = new URL(getCodeBase(), "ImgView.toc");
		DataInputStream dis= new DataInputStream(tocfile.openStream());
		String inputLine;
		boolean comment, file;
		
		while((inputLine = dis.readLine()) != null){

			StringBuffer f = new StringBuffer();
			StringBuffer c = new StringBuffer();

			if(inputLine.length()>1){ 
			   if(inputLine.charAt(0)=='*'){
			        comment=false;
			        file=true;
			        StringBuffer buff = new StringBuffer();
				for(int i=1; i<inputLine.length(); i++){

					fileName[noOfPhotos]="";
					comments[noOfPhotos]="";
	

					if((inputLine.charAt(i)=='*' || inputLine.charAt(i)=='>' || inputLine.charAt(i)==' ' || inputLine.charAt(i)=='\t') && file==true){
						file=false;
					}

					if(inputLine.charAt(i)=='>' && comment==false){
						comment=true;
						file=false;
					}
					
					if(file){
						f.append(inputLine.charAt(i));
					}
					
					if(comment){
						c.append(inputLine.charAt(i));
					}
				}

				fileName[noOfPhotos]=f.toString();
				notFound[noOfPhotos]=false;

				if (comment){				
					String cc = c.toString();
				
					for(int i=1; i<cc.length(); i++){
						buff.append(cc.charAt(i));
					}
				}
				else{
					String ff = f.toString();
					if((ff.lastIndexOf('/')>0)||(ff.lastIndexOf('/'))>0){
						for(int i=1+Math.max(ff.lastIndexOf('/'),ff.lastIndexOf('\\')); i<ff.length(); i++){
							buff.append(ff.charAt(i));
						}
					}
					else{
						for(int i=1+ff.lastIndexOf('*'); i<ff.length(); i++){
							buff.append(ff.charAt(i));
						}
					}
				}
				comments[noOfPhotos]=buff.toString();
				photoList.addItem(buff.toString());
				noOfPhotos++;
			   }
			}
		}
		
		dis.close();
	}catch(MalformedURLException e){
		System.out.println(e);
	}catch(IOException e){
		System.out.println(e);
	}catch(ArrayIndexOutOfBoundsException e){
		System.out.println("\nThe numberOfPhotos parameter in the <APPLET> tag is smaller\nthan the number of photos in the configuration file!\n\n"+e);
	}

	labTitle = new Label(" Contacting host...");
	
	if(!appletToSmall){
		photo = getImage(getCodeBase(), fileName[pointPhoto]);

		pSouth = new Panel();
		pBtn = new Panel();
		pControl = new Panel();
		pZoom = new Panel();
		p1Title = new Panel();
		pTitle = new Panel();
		pEast = new Panel();
		btnNext = new Button("Next");
		btnNext.setBackground(prevNextBtnCol);
		btnPrev = new Button("Previous");
		btnPrev.setBackground(prevNextBtnCol);
		btnFit = new Button("Fit");
		btnFit.setBackground(btnCol);
		btn1to1 = new Button("1 : 1");
		btn1to1.setBackground(btnCol);
		btnZoomIn = new Button("Zoom In");
		btnZoomIn.setBackground(btnCol);
		btnZoomOut = new Button("Zoom Out");
		btnZoomOut.setBackground(btnCol);
		btnInfo = new Button("Help");
		btnInfo.setBackground(btnCol);

		setLayout(new BorderLayout());
		pSouth.setLayout(new BorderLayout());
		pZoom.setLayout(new GridLayout(1,4));
		pTitle.setLayout(new BorderLayout());
		p1Title.setLayout(new BorderLayout());

		pTitle.add("North", horizScroll);

		photoList.select(0);
		p1Title.setBackground(labCol);
		p1Title.add("Center", labTitle);
		pTitle.add("Center", p1Title);

			pZoom.add(btnFit);
			pZoom.add(btn1to1);
			pZoom.add(btnZoomIn);
			pZoom.add(btnZoomOut);
			pBtn.setLayout(new GridLayout(2,1));
			
		if(layoutStyle=='c'){
			pControl.setLayout(new GridLayout(1,2));
			pControl.add(btnPrev);
			pControl.add(btnNext);
		}	
		else{

			pControl.setLayout(new BorderLayout());
			pControl.add("West", pBtn);
			pControl.add("Center", photoList);
			pBtn.add(btnPrev);
			btnPrev.disable();
			pBtn.add(btnNext);
		}

		p1Title.add("East", btnInfo);

		pSouth.add("Center", pControl);
		pSouth.add("South", pZoom);
		pSouth.add("North", pTitle);

		add("South", pSouth);
		add("East", vertScroll);
		vertScroll.disable();
		horizScroll.disable();
		btnFit.disable();
		btn1to1.disable();
		btnZoomIn.disable();
		btnZoomOut.disable();
		
		validate();

		picW=size().width-vertScroll.size().width;
		picH=vertScroll.size().height;
		getsize();
	}
	else{
		labTitle.setText("Applet too small!");
		add("Center", labTitle);
		validate();
	}



    }

 public boolean imageUpdate(Image theimg, int infoflags, int x, int y, int w, int h){

	if((infoflags & (ERROR))!=0){
		errored=true;
	}

	if(sizeknown == false){
		if ((infoflags & (WIDTH | HEIGHT)) !=0) {
			getsize();
		}
	}

	boolean done = ((infoflags & (ERROR | FRAMEBITS | ALLBITS)) != 0);
	repaint(done ? 0 : 200); //repaint every 200miliseconds
	return !done;
 }


 public synchronized void getsize(){

	if(errored){
		photo = getImage(getCodeBase(), "error.jpg");
		repaint();
		errored=false;
		String tmp = photoList.getItem(pointPhoto);
		if(notFound[pointPhoto]!=true){
			photoList.replaceItem("Not found: "+tmp, pointPhoto);
			comments[pointPhoto]="Not found: "+tmp;
			notFound[pointPhoto]=true;
		}
		photoList.select(pointPhoto);
	}

	if(sizeknown==false){	//here is the brain of th eapplet
				//this block calculates the size of the image
				
		pw1to1 = photo.getWidth(this);
		ph1to1 = photo.getHeight(this);
		pwFit=pw1to1;
		phFit=ph1to1;

		if( phFit<0 || pwFit<0){
			return;
		}
		sizeknown=true;
		if((phFit>(picH-4))||(pwFit>(picW-4))){ //the image is larger than the applet, must resize the image
			if( (phFit/pwFit) < (((float)picH-4)/((float)picW-4))){
				phFit=phFit/pwFit;
				pwFit=picW-4;
				phFit=phFit*pwFit;
				pxFit=2;
				pyFit=((picH-4)-Math.round(phFit))/2;
			}
			else{
				pwFit=pwFit/phFit;
				phFit=picH-4;
				pwFit=phFit*pwFit;
				pyFit=2;
				pxFit=((picW-4)-Math.round(pwFit))/2;
			}
		}
		else{ //the image is smaller than the applet
			pyFit=(picH-Math.round(phFit))/2;
			pxFit=(picW-Math.round(pwFit))/2;
		}
		px=pxFit;
		py=pyFit;
		pw=Math.round(pwFit);
		ph=Math.round(phFit);
		py1to1=(picH-Math.round(ph1to1))/2;
		px1to1=(picW-Math.round(pw1to1))/2;
	}
 }

 public synchronized void paint(Graphics g){
	btnFit.disable();
	btn1to1.disable();
	btnZoomIn.disable();
	btnZoomOut.disable();
	g.setColor(backCol);
	g.fillRect(0,0,picW,picH);
	update(g);
 }


 public synchronized void update(Graphics g) {
	getsize();
	if(pw<pwFit && ph<phFit){
		tooSmall=true;
	}
	else{
		tooSmall=false;
	}
	if( pw>pw1to1*maxZoom && ph>ph1to1*maxZoom ){
		tooBig=true;
	}
	else{
		tooBig=false;
	}
	if(sizeknown){
		if(!fitOn) btnFit.enable();
		if(!oneto1On) btn1to1.enable();
		if(!tooSmall) {btnZoomOut.enable();}
		if(!tooBig)   {btnZoomIn.enable();}
		labTitle.setText(" "+comments[pointPhoto]);
		g.drawImage(photo, px, py, pw, ph, this);
	}
  }
  

 public boolean action(Event e, Object arg){
		Object target = e.target;
		
		if((target == this.btnNext)&&(pointPhoto<(noOfPhotos-1))){
		        pointPhoto++;
			photo = getImage(getCodeBase(), fileName[pointPhoto]);
			photoList.select(pointPhoto);
			checkPoint();
			return true;
		}

		if((target == this.btnPrev)&&(pointPhoto>0)){
		        pointPhoto--;
			photo = getImage(getCodeBase(), fileName[pointPhoto]);
			photoList.select(pointPhoto);
			checkPoint();
			return true;
		}
		
		if(e.target == photoList){
			pointPhoto=photoList.getSelectedIndex();
			photo = getImage(getCodeBase(), fileName[pointPhoto]);
			checkPoint();
			return true;
		}

		if(e.target == this.btnInfo){
			helpWindow.show();
			return true;
		}

		if(target == this.btn1to1){
			oneto1On=true;
			fitOn=false;
			px=px1to1;
			py=py1to1;
			pw=Math.round(pw1to1);
			ph=Math.round(ph1to1);
			setScroll(px, py, pw, ph, picW, picH);
			paint(getGraphics());
			return true;
		}

		if(target == this.btnFit){
			oneto1On=false;
			fitOn=true;
			px=pxFit;
			py=pyFit;
			pw=Math.round(pwFit);
			ph=Math.round(phFit);
			setScroll(px, py, pw, ph, picW, picH);
			paint(getGraphics());
			return true;
		}

		if(target == this.btnZoomIn && !tooBig){
			btnZoomIn.disable();
			px=(int)(1.5*px-picW/4);
			py=(int)(1.5*py-picH/4);
			pw=(int)(pw*1.5);
			ph=(int)(ph*1.5);
			setScroll(px, py, pw, ph, picW, picH);
			oneto1On=false;
			fitOn=false;
			paint(getGraphics());
			return true;
		}

		if(target == this.btnZoomOut && !tooSmall){
			btnZoomOut.disable();
			px=(int)(px/1.5+picW/6);
			py=(int)(py/1.5+picH/6);
			pw=(int)(pw/1.5);
			ph=(int)(ph/1.5);

			if(px>0){
				if(pw<picW){
					px=(picW-pw)/2;
				}
				else{
					px=0;
				}
			}
			if(px+pw<picW){
				if(pw>picW){
					px=picW-pw;
				}
				else{
					px=(picW-pw)/2;
				}
			}
			if(py>0){
				if(ph<picH){
					py=(picH-ph)/2;
				}
				else{
					py=0;
				}
			}
			if(py+ph<picH){
				if(ph>picH){
					py=picH-ph;
				}
				else{
					py=(picH-ph)/2;
				}
			}

			setScroll(px, py, pw, ph, picW, picH);
			oneto1On=false;
			fitOn=false;
			paint(getGraphics());
			return true;
		}
		return false;		
	}

	public boolean handleEvent(Event evt){

		switch(evt.id){
			case Event.SCROLL_LINE_UP:
			case Event.SCROLL_LINE_DOWN:
			case Event.SCROLL_PAGE_UP:
			case Event.SCROLL_PAGE_DOWN:
			case Event.SCROLL_ABSOLUTE:
				if(evt.target == horizScroll){
					px=-((Integer)evt.arg).intValue();
					if(px>=0){
						px=0;
					}
					if(px<=picW-pw){
						px=picW-pw;
					}
					repaint(0,0,picW,picH);
				}
				if(evt.target == vertScroll){
					py=-((Integer)evt.arg).intValue();
					if(py>=0){
						py=0;
					}
					if(py<=picH-ph){
						py=picH-ph;
					}
					repaint(0,0,picW,picH);
				}
		}
		return super.handleEvent(evt);
	}

	private void setScroll(int x, int y, int w, int h, int pW, int pH){

		horizScroll.disable();
		vertScroll.disable();

		if( (x<0) || ((x+w)>picW)){
			horizScroll.setValues(-x, 1, 0, w-pW);
			horizScroll.setPageIncrement(16);
			horizScroll.setLineIncrement(4);
			horizScroll.enable();
		}

		if( (y<0) || ((y+h)>picH)){
			vertScroll.setValues(-y, 1, 0, h-pH);
			vertScroll.setPageIncrement(16);
			vertScroll.setLineIncrement(4);
			vertScroll.enable();
		}
	}

	private void checkPoint(){ //Check what buttons should be enabled or desabled
		labTitle.setText(" Contacting host...");
		if(pointPhoto<=(noOfPhotos-2)){
			btnNext.enable();
		}
		else{
			btnNext.disable();
		}
		if(pointPhoto>=1){
			btnPrev.enable();
		}
		else{
			btnPrev.disable();
		}

		sizeknown=false;

		btnFit.disable();
		btn1to1.disable();
		btnZoomIn.disable();
		btnZoomOut.disable();

		horizScroll.disable();
		vertScroll.disable();
		tooBig=false;
		tooSmall=false;
		oneto1On=false;
		fitOn=true;
		paint(getGraphics());
	}

	private String setAboutText(){
		String copyright=VERSION+" - image browser applet\n"+COPYRIGHT+" "+AUTHOR+"\n\nThis program is free software; you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation; either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\nYou can contact me at:\n\n* http://www.obs-us.com/people/mihai\n* e-mail:  m.munteanu@shef.ac.uk   or   mmm@deltanet.roknet.ro";
		String help=VERSION+" - Help\n\nBrowsing Controls:\n\n* Previous - Loads and displays the previous picture\n* Next - Loads and displays the next picture\n* Picture List - Double click on the chosen title to load and display the picture\n\nNOTE:\nThe picture list is not displayed in the compact version of the layout\n\nZoom Controls:\n\n* Fit - Fits the picture in the display area\n* 1 : 1 - Displays the picture at its original size\n* Zoom In - Magnifies the picture size 1.5 times\n* Zoom In - Reduces the picture size 1.5 times\n\nNOTES:\n* If the image is smaller than the display area '1 : 1' and 'Fit' produce the same result.\n* The number of zooming steps is limited.\n\n\n";
		return help+copyright;
	}
	
	public String getAppletInfo() {
		return VERSION+"  "+COPYRIGHT+" "+AUTHOR+" - mmm@deltanet.roknet.ro";
	}
}
